package com.sykj.seaflow.warm.module.def.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.sykj.seaflow.warm.core.bean.FieldSetting;
import com.sykj.seaflow.warm.core.enums.FormFieldPermissionEnum;
import com.sykj.seaflow.warm.module.def.params.SeaFlowDefinitionPageParam;
import com.sykj.seaflow.warm.module.design.service.SeaFlowDesignService;
import com.sykj.seaflow.warm.module.nodeExt.entity.FlowNodeExt;
import com.sykj.seaflow.warm.module.nodeExt.service.FlowNodeExtService;
import org.dromara.warm.flow.core.entity.Definition;
import org.dromara.warm.flow.core.enums.NodeType;
import org.dromara.warm.flow.core.service.DefService;
import org.dromara.warm.flow.core.service.NodeService;
import org.dromara.warm.flow.core.utils.page.Page;
import org.dromara.warm.flow.orm.entity.FlowDefinition;
import lombok.AllArgsConstructor;
import com.sykj.seaflow.common.base.bean.PageQuery;
import com.sykj.seaflow.warm.module.category.entity.FlowCategory;
import com.sykj.seaflow.warm.module.category.service.FlowCategoryService;
import com.sykj.seaflow.warm.module.def.entity.FlowApproval;
import com.sykj.seaflow.warm.module.def.entity.FlowDefExt;
import com.sykj.seaflow.warm.module.def.entity.SeaFlowDefinition;
import com.sykj.seaflow.warm.module.def.params.SeaFlowDefinitionPageParam;
import com.sykj.seaflow.warm.module.def.service.FlowDefExtService;
import com.sykj.seaflow.warm.module.def.service.FlowDefinitionService;
import com.sykj.seaflow.warm.module.design.entity.SeaFlowDesign;
import com.sykj.seaflow.warm.module.design.params.SeaFlowDesignPageParam;
import com.sykj.seaflow.warm.module.design.service.SeaFlowDesignService;
import org.dromara.warm.flow.orm.entity.FlowNode;
import org.dromara.warm.flow.orm.mapper.FlowNodeMapper;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@AllArgsConstructor
@Service
public class FlowDefinitionServiceImpl implements FlowDefinitionService {
    private final DefService defService;
    private final FlowNodeMapper flowNodeMapper;
    private final FlowNodeExtService nodeExtService;
    private final SeaFlowDesignService designService;
    private final FlowCategoryService categoryService;
    private final FlowDefExtService defExtService;


    @Override
    public IPage<Definition> page(SeaFlowDefinitionPageParam flowDefinitionPageParam, PageQuery<SeaFlowDefinition> pageQuery) {
//        Definition definition = BeanUtil.copyProperties(flowDefinitionPageParam, FlowDefinition.class);
        FlowDefinition flowDefinition = new FlowDefinition();
        if(ObjectUtil.isNotEmpty(flowDefinitionPageParam.getFlowCode())){
            flowDefinition.setFlowCode(flowDefinitionPageParam.getFlowCode());
        }
        if(ObjectUtil.isNotEmpty(flowDefinitionPageParam.getFlowName())){
            flowDefinition.setFlowName(flowDefinitionPageParam.getFlowName());
        }

        Page<Definition> page = new Page<>(pageQuery.getCurrent(), pageQuery.getSize());
        //page 转化
        List<OrderItem> orders = pageQuery.getPage().orders();
        if(ObjectUtil.isNotEmpty(orders)){
            String column = orders.get(0).getColumn();
            page.setOrderBy(column);
            page.setIsAsc(orders.get(0).isAsc()? "asc" :"desc");
        }
        Page<Definition> page1 = defService.page(flowDefinition, page);
        PageDTO<Definition> pageDto = new PageDTO<>(page1.getPageNum(), page1.getPageSize(), page1.getTotal());
        pageDto.setRecords(page1.getList());
        return pageDto;
    }

    @Override
    public List<FlowApproval> list(SeaFlowDefinitionPageParam flowDefinitionPageParam) {
        List<FlowApproval> flowApprovals = new ArrayList<>();
        List<FlowCategory> categories = categoryService.list(Wrappers.<FlowCategory>lambdaQuery().orderByAsc(FlowCategory::getSortNum));
        if(ObjectUtil.isNotEmpty(categories)){
            // 获取设计中的 发布流程  。 可以有很多个设计， 但是只获取已发布过的
            List<SeaFlowDesign> list = designService.canUseFlow(new SeaFlowDesignPageParam());
            Map<Long, List<SeaFlowDesign>> designGroup = list.stream().collect(Collectors.groupingBy(SeaFlowDesign::getCategoryId));
            // 拿到流程定义id，去查询流程
            List<Long> defIds = list.stream().filter(ObjectUtil::isNotEmpty).map(SeaFlowDesign::getDefId).collect(Collectors.toList());// .toList();
            if(ObjectUtil.isNotEmpty(defIds)){
                // 流程定义和 扩展信息，（对应版本的流程定义）
                List<Definition> definitions = defService.getByIds(defIds);
                List<FlowDefExt> flowDefExts = defExtService.listByIds(defIds);
                Map<Long, FlowDefExt> flowDefExtMap = flowDefExts.stream().collect(Collectors.toMap(FlowDefExt::getId, Function.identity()));
                // 提取id
                Map<Long, Definition> defMap = definitions.stream().collect(Collectors.toMap(Definition::getId, Function.identity()));
                // 获取所有开始节点，得到表单配置项
                List<FlowNode> flowNodes = flowNodeMapper.selectList(Wrappers.<FlowNode>lambdaQuery().eq(FlowNode::getNodeType, NodeType.START.getKey()).in(FlowNode::getDefinitionId, defIds));
                Map<Long, FlowNode> defNodes = flowNodes.stream().collect(Collectors.toMap(FlowNode::getDefinitionId, Function.identity()));
                List<FlowNodeExt> nodeExts = nodeExtService.list(Wrappers.<FlowNodeExt>lambdaQuery().eq(FlowNodeExt::getNodeType, NodeType.START.getKey()).in(FlowNodeExt::getDefId, defIds));
                //节点扩展信息
                Map<Long, FlowNodeExt> defNodeExts = nodeExts.stream().collect(Collectors.toMap(FlowNodeExt::getDefId, Function.identity()));
                // 流程分类

                for(FlowCategory category: categories){
                    // 当前分类下的流程设计
                    List<SeaFlowDesign> seaFlowDesigns = designGroup.get(category.getId());
                    if(ObjectUtil.isNotEmpty(seaFlowDesigns)){
                        List<SeaFlowDesign> categoryDefs = seaFlowDesigns.stream().map(r -> {
                            Definition definition = defMap.get(r.getDefId());
                            // 流程设计表的数据 前端配置会有变动， 而 Definition 发布后数据就不会变了
                            SeaFlowDesign seaFlowDesign = BeanUtil.copyProperties(definition, SeaFlowDesign.class);
                            FlowDefExt flowDefExt = flowDefExtMap.get(seaFlowDesign.getId());
                            if(ObjectUtil.isNotNull(flowDefExt)){
                                seaFlowDesign.setFlowJson(flowDefExt.getFlowJson());
                                seaFlowDesign.setFormJson(flowDefExt.getFormJson());
                            }
                            seaFlowDesign.setFlowVersion(definition.getVersion());
                            seaFlowDesign.setIcon(r.getIcon());
                            seaFlowDesign.setSortNum(r.getSortNum());
                            // 开始节点
                            FlowNode flowNode = defNodes.get(seaFlowDesign.getId());
                            if(flowNode != null){
                                String formPath = flowNode.getFormPath();
                                if(ObjectUtil.isNotEmpty(formPath)){
                                    // 修改formpath
                                    seaFlowDesign.setFormPath(formPath);
                                }
                            }
                            FlowNodeExt flowNodeExt = defNodeExts.get(seaFlowDesign.getId());
                            if(ObjectUtil.isNotNull(flowNodeExt)){
                                String fieldSetting = flowNodeExt.getFieldSetting();
                                String formJson = seaFlowDesign.getFormJson();
                                // json处理
                                if(ObjectUtil.isNotEmpty(fieldSetting) && JSONUtil.isTypeJSON(formJson)){
                                    JSONObject pageSchema = JSONUtil.parseObj(formJson);
                                    JSONArray jsonArray = pageSchema.getJSONArray("schemas").getJSONObject(0).getJSONArray("children");
                                    List<FieldSetting> fieldSettingList = JSONUtil.toList(fieldSetting, FieldSetting.class);
                                    formControl(fieldSettingList,jsonArray);
                                    seaFlowDesign.setFormJson(pageSchema.toString());
                                }

                            }


                            return seaFlowDesign;
                        }).filter(ObjectUtil::isNotEmpty).collect(Collectors.toList());//.toList();
                        FlowApproval flowApproval = new FlowApproval(category, categoryDefs);

                        flowApprovals.add(flowApproval);
                    }else{
                        flowApprovals.add(new FlowApproval(category, new ArrayList<>()));
                    }

                }

            }
        }

        return flowApprovals;
    }

    private void formControl( List<FieldSetting> fieldSettingList, JSONArray jsonArray) {
        for(Object field : jsonArray){
            JSONObject f =  (JSONObject)field;
            if(f.getBool("input")){
                FieldSetting matchFeild = matchFeild(fieldSettingList, f);
                JSONObject componentProps = f.getJSONObject("componentProps");
                if(componentProps == null){
                    componentProps =  new JSONObject();
                    f.set("componentProps",componentProps);
                }
                // 判断是否匹配上
                if( matchFeild != null){
                    FormFieldPermissionEnum permission = FormFieldPermissionEnum.getPermission(matchFeild.getPermission());
                    if(permission != null){
                        if(permission ==  FormFieldPermissionEnum.EDIT ){
                            componentProps.set(FormFieldPermissionEnum.READONLY.getValue(), false);
                            componentProps.set(FormFieldPermissionEnum.HIDDEN.getValue(), false);
                        }
                        componentProps.set(permission.getValue(), true);
                    }
                }
            }else{
                JSONArray jsonArray1 = f.getJSONArray("children");
                formControl(fieldSettingList, jsonArray1);
            }
        }
    }
    private FieldSetting matchFeild(List<FieldSetting> fields, JSONObject field){
        for(FieldSetting f : fields){
            if(ObjectUtil.equal(f.getField(), field.getStr("field"))){
                return f;
            }
        }
        return null;
    }


    private void putDef(Map<String, List<Definition>> result, String categoryId, Definition definition) {
        List<Definition> definitions = result.computeIfAbsent(categoryId, k -> new ArrayList<>());
        definitions.add(definition);
    }
}
